package com.uuah.server.config.spring.parsers;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

import com.uuah.server.config.spring.parsers.assembly.IBeanAssembler;
import com.uuah.server.config.spring.parsers.generic.AutoIdUtils;
import com.uuah.server.util.spring.SpringXMLUtils;
import com.uuah.utils.StringUtils;

/**
 * This definition parser supports the definition of beans that are then set on
 * the parent bean - it extends
 * {@link com.uuah.server.config.spring.parsers.AbstractHierarchicalDefinitionParser}
 * with methods that assume the data are associated with a single property.
 *
 * This supports collections and Maps. For collections if a child element is
 * repeated it will be assumed that it is a collection.
 *
 * If the Bean Class for this element is set to
 * {@link com.uuah.server.config.spring.parsers.collection.ChildMapEntryDefinitionParser.KeyValuePair}
 * it is assumed that a Map is being processed and any child elements will be
 * added to the parent Map. Similarly for
 * {@link com.uuah.server.config.spring.parsers.collection.ChildListEntryDefinitionParser}
 * .
 *
 * A single method needs to be overriden called {@link #getPropertyName} that
 * determines the name of the property to set on the parent bean with this bean.
 * Note that the property name can be dynamically resolved depending on the
 * parent element.
 *
 * @see com.uuah.server.config.spring.parsers.generic.ChildDefinitionParser
 * @see com.uuah.server.config.spring.parsers.collection.ChildMapEntryDefinitionParser.KeyValuePair
 * @see AbstractUuahBeanDefinitionParser
 */
public abstract class AbstractChildDefinitionParser extends
		AbstractHierarchicalDefinitionParser implements
		UuahChildDefinitionParser {

	protected final void doParse(Element element, ParserContext parserContext,
			BeanDefinitionBuilder builder) {
		setRegistry(parserContext.getRegistry());
		parseChild(element, parserContext, builder);
	}

	protected void parseChild(Element element, ParserContext parserContext,
			BeanDefinitionBuilder builder) {
		builder.setScope(isSingleton() ? BeanDefinition.SCOPE_SINGLETON
				: BeanDefinition.SCOPE_PROTOTYPE);
		super.doParse(element, parserContext, builder);
	}

	protected void postProcess(ParserContext context, IBeanAssembler assembler,
			Element element) {
		super.postProcess(context, assembler, element);

		// legacy handling of orphan beans - avoid setting parent
		String propertyName = getPropertyName(element);
		if (null != propertyName) {
			// If this is a singleton we need to inject it into parent using a
			// RuntimeBeanReference so that the bean does not get created twice,
			// once
			// with a name and once as an (inner bean).
			if (!assembler.getBean().getBeanDefinition().isSingleton()) {
				assembler.insertBeanInTarget(propertyName);
			} else {
				assembler
						.getTarget()
						.getPropertyValues()
						.addPropertyValue(
								propertyName,
								new RuntimeBeanReference(
										element
												.getAttribute(AbstractUuahBeanDefinitionParser.ATTRIBUTE_NAME)));
			}
		}
	}

	public String getBeanName(Element e) {
		String name = SpringXMLUtils.getNameOrId(e);
		if (StringUtils.isBlank(name)) {
			String parentId = getParentBeanName(e);
			if (!parentId.startsWith(".")) {
				parentId = "." + parentId;
			}
			return AutoIdUtils.uniqueValue(parentId + ":" + e.getLocalName());
		} else {
			return name;
		}
	}

	public abstract String getPropertyName(Element element);

}
